﻿using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using System.Security.Cryptography;
using System.Text;

namespace SenGe.Domain.Utils
{
    /// <summary>
    /// 加密解密帮助类
    /// </summary>
    public class CryptoUtil : IDisposable
    {
        #region 普通算法Base64

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="str">需要加密的字符串</param>
        /// <returns>加密后的字符串</returns>
        public static string Base64Encrypt(string str)
        {
            if (string.IsNullOrEmpty(str)) return str;
            return Base64Encrypt(str, Encoding.UTF8);
        }

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="str">需要加密的字符串</param>
        /// <param name="encode">字符编码</param>
        /// <returns>加密后的字符串</returns>
        public static string Base64Encrypt(string str, Encoding encode)
        {
            if (string.IsNullOrEmpty(str)) return str;
            return Convert.ToBase64String(encode.GetBytes(str));
        }

        /// <summary>
        /// Base64解密
        /// </summary>
        /// <param name="str">需要解密的字符串</param>
        /// <returns>解密后的字符串</returns>
        public static string Base64Decrypt(string str)
        {
            if (string.IsNullOrEmpty(str)) return str;
            return Base64Decrypt(str, Encoding.UTF8);
        }

        /// <summary>
        /// Base64解密
        /// </summary>
        /// <param name="str">需要解密的字符串</param>
        /// <param name="encode">字符的编码</param>
        /// <returns>解密后的字符串</returns>
        public static string Base64Decrypt(string str, Encoding encode)
        {
            if (string.IsNullOrEmpty(str)) return str;
            return encode.GetString(Convert.FromBase64String(str));
        }

        #endregion

        #region 对称加密 (AES-256加密算法 | 已弃用的DES加密算法)
        /// <summary>
        /// AES加密Key
        /// 32字节，每个字节8位共256位
        /// </summary>
        private static readonly byte[] MyAESKey = Convert.FromBase64String("+B+QVvNPcWbpo9XG+J3T6mlRlfnMEUtwO4xGHm1dxmc=");
        /// <summary>
        /// AES加密IV
        /// 16字节，每个字节8位共128位
        /// </summary>
        private static readonly byte[] MyAESIV = Convert.FromBase64String("396q3KVcQ+7M+7Ogipjdww==");

        /// <summary>
        /// 生成加密key和iv的随机算法
        /// 运行一次直接写死在MyKey和MyIV中
        /// </summary>
        /// <returns></returns>
        private static (string, string) GenAESKeyIV()
        {
            var aesKey = new byte[32]; // 256-bit key
            var aesIV = new byte[16];  // 128-bit IV
            RandomNumberGenerator.Fill(aesKey);
            RandomNumberGenerator.Fill(aesIV);

            return (Convert.ToBase64String(aesKey), Convert.ToBase64String(aesIV));
        }

        /// <summary>
        /// 加密字符串
        /// </summary>
        /// <param name="str">需要加密的字符串</param>
        /// <returns>加密后的字符串</returns>
        public static string AESEncrypt(string str)
        {
            if (string.IsNullOrEmpty(str)) return string.Empty;
            return Convert.ToBase64String(AES_Encrypt(Encoding.UTF8.GetBytes(str), MyAESKey, MyAESIV));
        }

        /// <summary>
        /// AES-256加密 (推荐使用)
        /// </summary>
        /// <param name="data">加密字节流</param>
        /// <param name="key">加密Key字节流</param>
        /// <param name="iv">加密IV字节流</param>
        /// <returns></returns>
        private static byte[] AES_Encrypt(byte[] data, byte[] key, byte[] iv)
        {
            using var aes = Aes.Create();
            aes.KeySize = 256;
            aes.BlockSize = 128;
            aes.Key = key;
            aes.IV = iv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            using var encryptor = aes.CreateEncryptor();
            return encryptor.TransformFinalBlock(data, 0, data.Length);
        }

        /// <summary>
        /// 解密码字符串
        /// </summary>
        /// <param name="str">需要加密的字符串</param>
        /// <returns>解密后的字符串</returns>
        public static string AESDecrypt(string str)
        {
            if (string.IsNullOrEmpty(str)) return string.Empty;
            return Encoding.UTF8.GetString(AES_Decrypt(Convert.FromBase64String(str), MyAESKey, MyAESIV));
        }

        /// <summary>
        /// AES-256解密 (推荐使用)
        /// </summary>
        /// <param name="cipherData">解密字节流</param>
        /// <param name="key">加密Key字节流</param>
        /// <param name="iv">加密IV字节流</param>
        /// <returns></returns>
        private static byte[] AES_Decrypt(byte[] cipherData, byte[] key, byte[] iv)
        {
            using var aes = Aes.Create();
            aes.KeySize = 256;
            aes.BlockSize = 128;
            aes.Key = key;
            aes.IV = iv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            using var decryptor = aes.CreateDecryptor();
            return decryptor.TransformFinalBlock(cipherData, 0, cipherData.Length);
        }

        /// <summary>
        /// DES加密 (已过时，不推荐)
        /// </summary>
        private static byte[] DES_Encrypt(byte[] data, byte[] key, byte[] iv)
        {
            using var des = DES.Create();
            des.Key = key;
            des.IV = iv;
            des.Mode = CipherMode.CBC;
            des.Padding = PaddingMode.PKCS7;

            using var encryptor = des.CreateEncryptor();
            return encryptor.TransformFinalBlock(data, 0, data.Length);
        }

        /// <summary>
        /// DES解密
        /// </summary>
        private static byte[] DES_Decrypt(byte[] cipherData, byte[] key, byte[] iv)
        {
            using var des = DES.Create();
            des.Key = key;
            des.IV = iv;
            des.Mode = CipherMode.CBC;
            des.Padding = PaddingMode.PKCS7;

            using var decryptor = des.CreateDecryptor();
            return decryptor.TransformFinalBlock(cipherData, 0, cipherData.Length);
        }

        #endregion

        #region 非对称加密 RSA2048
        /// <summary>
        /// RSA加密公钥
        /// </summary>
        private static readonly string MyPublicKey = @"
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA5svJeWUCBb5ygj3CRywmnT7232RqYIp7A98Q6Gvq3hNgcswqH6qb
m3ZgzRP2I/pq2JAlLNamJ6UnEls90V+iNzuzPfrtjn2yE+dYQPLdZI5RfPgx7Qs9
Uqwe6E5Syg5rucjkMIClFwYe6XxyqRNTNt9lafDQho8abFolZ01jfhv5O9yU8Kn4
gdhuWroxqCw/rex2JtntQBduQCCADwILjVsb90zHCzIQJdK7JRahfMqqPpTFCO8N
JVwqoa7nfU7skqR1fgQT4Wh9G610TZLftIXo0DBG8pkCRU91BIkREu5isjZoFC66
UzVdUkpPSrjpifeDb9IO3MMDAk6Y8gPVVQIDAQAB
-----END RSA PUBLIC KEY-----";
        /// <summary>
        /// RSA解密私钥
        /// </summary>
        private static readonly string MyPrivateKey = @"
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDmy8l5ZQIFvnKC
PcJHLCadPvbfZGpginsD3xDoa+reE2ByzCofqpubdmDNE/Yj+mrYkCUs1qYnpScS
Wz3RX6I3O7M9+u2OfbIT51hA8t1kjlF8+DHtCz1SrB7oTlLKDmu5yOQwgKUXBh7p
fHKpE1M232Vp8NCGjxpsWiVnTWN+G/k73JTwqfiB2G5aujGoLD+t7HYm2e1AF25A
IIAPAguNWxv3TMcLMhAl0rslFqF8yqo+lMUI7w0lXCqhrud9TuySpHV+BBPhaH0b
rXRNkt+0hejQMEbymQJFT3UEiRES7mKyNmgULrpTNV1SSk9KuOmJ94Nv0g7cwwMC
TpjyA9VVAgMBAAECggEAAUrc2vgs1blmTMJpxxpBMRo8Ka4XRmkgHTpvjs2j6BB1
ts7b4YeEDY/RJPTaqQLq/EF6Y9Dj507udBTcuMJvVx3XeB5inCyqBaCDBwIVKU3+
uNNu/CPW+RKLrcNahVUlV0n/tSHTSti27ZnLzpSUH7eKithVYpuEi1zJ3tU5HGiq
+Bj+NbbPdIMF405aghO8sNbW8CX5v44zX2h4/V1wApetSpW/apRMAMvQvidzYGLZ
7p6IlsMxaYg+nmNW7vaa2oI4j2In28Wh4XO/10PpmwVPhPYQjvaQtsqcc8tMUHW2
y2KXSydpjt4rK5RpRd3TFVClObJk5d1+2DINbA7cNQKBgQDpK2ycP+6rB0ztXzqW
iRgdIhzKwiUEEK/ztHDLmmuXBn9c/ESycO1eP9DhYy5CylIIYqKEDcPpmXNMRjBw
Oax4KH/DdaSYClX4Nwuu8UWNXV83jW5DO3G1CS3WfuBbFfgBARoZ34bYqPHhtsUh
ha6ulkpcK97QYQXemRZ1JUigIwKBgQD9ZN34MnYgnGQpjzXLpjtcCmUZnUSthe+M
9G0pDyUw1/4BwzNT2fH6YOb3xfVA0QEhLqExbbfr7zNmoqxHnqNHsT3CWi8vKQpk
DKZzCinWOTcOiuU6GtEH7cd3LT6bpnJeIoq9/cExsAjcvf31Exjp8GC+ZQAVJaqC
y6U5WL3QJwKBgGxpI0N2ujpM16iaGTx6m3qYLLjdEabVN2nhIHyZ3KJIhBkwtaQd
u0+R7kWM/qpRNVjuu08Ys1k+Xow9KjJTrmvjsJupbBDA5X3V4hnB1ivHGQlUOcsK
rXCVxvku3pObIEcfQwp6klDUJDpk5z0R0ckc2ZAHWb6bDcaUeQuYfS7vAoGBANNf
qd4Z0fpgGxmj8f8getHLhgx46UpKuMp/NvQSQzbcZnH38EAZRUZUQTcc9foMnwGc
ETDXUp5IWjtapZXU2mW6j0cpzcRsWtOczdiW+NFPxwKlkJQIvKf9jO94WYqjrTGj
ZNMZ82fMbYgeINOoC5dSQpFwVUJOuk8DK5+LzELvAoGBALf+rgZkHW6Pqfd08Vsv
VudVQPhHAlLNuIcbJT/e4y7JJq0OgrNoSUmNwGgMFDrKV2/s39dNE12/fX7Zdx6u
MGfHhOsPcj4t88wYqmck/3mryoH26WQHv0FwokJBtIT7NARZMGdnryC7Ouhk0Y8f
/qwtGoJU7dzsUOkejO+5MAwd
-----END PRIVATE KEY-----";

        /// <summary>
        /// RSA-2048加密
        /// </summary>
        /// <param name="str">加密内容</param>
        /// <returns></returns>
        public static string RSAEncrypt(string str)
        {
            if (string.IsNullOrEmpty(str)) return string.Empty;
            return Convert.ToBase64String(RSA_Encrypt(Encoding.UTF8.GetBytes(str), MyPublicKey));
        }

        /// <summary>
        /// RSA-2048加密
        /// </summary>
        /// <param name="data">加密数据字节流</param>
        /// <param name="publicKey">公钥</param>
        /// <returns></returns>
        private static byte[] RSA_Encrypt(byte[] data, string publicKey)
        {
            using var rsa = RSA.Create();
            rsa.ImportFromPem(publicKey);
            return rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
        }

        /// <summary>
        /// RSA-2048加密
        /// </summary>
        /// <param name="str">解密内容</param>
        /// <returns></returns>
        public static string RSADecrypt(string str)
        {
            if (string.IsNullOrEmpty(str)) return string.Empty;
            return Encoding.UTF8.GetString(RSA_Decrypt(Convert.FromBase64String(str), MyPrivateKey));
        }

        /// <summary>
        /// RSA-2048解密
        /// </summary>
        /// <param name="cipherData">密文字节流</param>
        /// <param name="privateKey">私钥</param>
        /// <returns></returns>
        private static byte[] RSA_Decrypt(byte[] cipherData, string privateKey)
        {
            using var rsa = RSA.Create();
            rsa.ImportFromPem(privateKey);
            return rsa.Decrypt(cipherData, RSAEncryptionPadding.OaepSHA256);
        }

        /// <summary>
        /// 生成RSA-2048密钥对
        /// 使用时，修改为public方法，然后生成一次写入只读变量中
        /// </summary>
        private static (string publicKey, string privateKey) GenerateRSAKeys(int keySize = 2048)
        {
            using var rsa = RSA.Create(keySize);
            return (
                rsa.ExportRSAPublicKeyPem(),
                rsa.ExportPkcs8PrivateKeyPem()
            );
        }

        #endregion

        #region 国密算法(非对称加密算法SM2 | 哈希算法SM3 | 对称加密算法SM4)
        /// <summary>
        /// SM4加密Key
        /// 16字节，每个字节8位共128位
        /// </summary>
        private static readonly byte[] MySM4Key = Convert.FromBase64String("7VM+R+KfaI363LeCmCtJsw==");
        /// <summary>
        /// SM4加密IV
        /// 16字节，每个字节8位共128位
        /// </summary>
        private static readonly byte[] MySM4IV = Convert.FromBase64String("T5Pql68kcPrjkthsCY/23g==");

        /// <summary>
        /// 使用SM4加密字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string SM4Encrypt(string str)
        {
            return Convert.ToBase64String(SM4_Encrypt(Encoding.UTF8.GetBytes(str), MySM4Key, MySM4IV));
        }

        /// <summary>
        /// 对称加密算法SM4加密 (国标)
        /// </summary>
        /// <param name="data">加密字节流</param>
        /// <param name="key">加密Key字节流</param>
        /// <param name="iv">加密IV字节流</param>
        /// <returns></returns>
        private static byte[] SM4_Encrypt(byte[] data, byte[] key, byte[] iv)
        {
            // 参数验证
            if (key.Length != 16)
                throw new ArgumentException("SM4 key must be 16 bytes (128 bits)");
            if (iv.Length != 16)
                throw new ArgumentException("SM4 IV must be 16 bytes (128 bits)");

            // 使用CBC模式并添加PKCS7填充
            var cipher = new PaddedBufferedBlockCipher(
                new CbcBlockCipher(new SM4Engine()),
                new Pkcs7Padding()
            );

            var keyParam = new KeyParameter(key);
            var keyParamWithIv = new ParametersWithIV(keyParam, iv);
            cipher.Init(true, keyParamWithIv);
            var output = new byte[cipher.GetOutputSize(data.Length)];

            int length = cipher.ProcessBytes(data, 0, data.Length, output, 0);
            cipher.DoFinal(output, length); // 处理填充

            return output;
        }

        /// <summary>
        /// 使用SM4解密字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string SM4Decrypt(string str)
        {
            return Encoding.UTF8.GetString(SM4_Decrypt(Convert.FromBase64String(str), MySM4Key, MySM4IV));
        }

        /// <summary>
        /// 对称加密算法SM4解密 (国标)
        /// </summary>
        /// <param name="cipherData">解密字节流</param>
        /// <param name="key">加密Key字节流</param>
        /// <param name="iv">加密IV字节流</param>
        /// <returns></returns>
        private static byte[] SM4_Decrypt(byte[] cipherData, byte[] key, byte[] iv)
        {
            // 参数验证
            if (key.Length != 16)
                throw new ArgumentException("SM4 key must be 16 bytes (128 bits)");
            if (iv.Length != 16)
                throw new ArgumentException("SM4 IV must be 16 bytes (128 bits)");

            // 使用CBC模式并添加PKCS7填充
            var cipher = new PaddedBufferedBlockCipher(
                new CbcBlockCipher(new SM4Engine()),
                new Pkcs7Padding()
            );

            var keyParam = new KeyParameter(key);
            var keyParamWithIv = new ParametersWithIV(keyParam, iv);

            cipher.Init(false, keyParamWithIv);
            var output = new byte[cipher.GetOutputSize(cipherData.Length)];

            int length = cipher.ProcessBytes(cipherData, 0, cipherData.Length, output, 0);
            length += cipher.DoFinal(output, length); // 处理填充

            // 移除填充
            byte[] result = new byte[length];
            Buffer.BlockCopy(output, 0, result, 0, length);
            return result;
        }

        /// <summary>
        /// 使用SM3加密字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string SM3Hash(string str)
        {
            byte[] hash = SM3_Hash(Encoding.UTF8.GetBytes(str));
            return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
        }

        /// <summary>
        /// SM3哈希 (国标)
        /// </summary>
        /// <param name="data">加密字节流</param>
        /// <returns></returns>
        public static byte[] SM3_Hash(byte[] data)
        {
            var digest = new SM3Digest();
            digest.BlockUpdate(data, 0, data.Length);
            var output = new byte[digest.GetDigestSize()];
            digest.DoFinal(output, 0);
            return output;
        }

        /// <summary>
        /// SM2加密公钥
        /// </summary>
        private static readonly byte[] MySM2PublicKey = Convert.FromBase64String("BIuBkXJQGIlxSyNfKT+Wi5QMWE3CFmTGRVjZ9YXzHC4VL73zRu0VgydSg15soQiH+FwQLIbRbJ7voh/s6Slzw28=");
        /// <summary>
        /// SM2加密私钥
        /// </summary>
        private static readonly byte[] MySM2PrivateKey = Convert.FromBase64String("BpxEC93V/TcqP0BA3cGX7b1pJbElt2Tp/boyde1WzfE=");

        /// <summary>
        /// 生成SM2密钥对
        /// 使用是通过此方法生成公钥和私钥
        /// </summary>
        private static (byte[] publicKey, byte[] privateKey) GenerateSM2Keys()
        {
            var curve = GMNamedCurves.GetByName("sm2p256v1");
            var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);

            var gen = new ECKeyPairGenerator();
            gen.Init(new ECKeyGenerationParameters(domainParams, new SecureRandom()));

            AsymmetricCipherKeyPair keyPair = gen.GenerateKeyPair();
            var privateKey = ((ECPrivateKeyParameters)keyPair.Private).D.ToByteArrayUnsigned();
            var publicKey = ((ECPublicKeyParameters)keyPair.Public).Q.GetEncoded(false);

            return (publicKey, privateKey);
        }

        /// <summary>
        /// 使用SM2加密字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string SM2Encrypt(string str)
        {
            return Convert.ToBase64String(SM2_Encrypt(Encoding.UTF8.GetBytes(str), MySM2PublicKey));
        }

        /// <summary>
        /// 非对称加密算法SM2加密
        /// </summary>
        /// <param name="data">加密字节流</param>
        /// <param name="publicKey">公钥字节流</param>
        /// <returns></returns>
        private static byte[] SM2_Encrypt(byte[] data, byte[] publicKey)
        {
            var curve = GMNamedCurves.GetByName("sm2p256v1");
            var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
            var q = curve.Curve.DecodePoint(publicKey);
            var pubKey = new ECPublicKeyParameters(q, domainParams);

            var engine = new SM2Engine();
            engine.Init(true, new ParametersWithRandom(pubKey, new SecureRandom()));

            return engine.ProcessBlock(data, 0, data.Length);
        }

        /// <summary>
        /// 使用SM2解密字符串
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string SM2Decrypt(string str)
        {
            return Encoding.UTF8.GetString(SM2_Decrypt(Convert.FromBase64String(str), MySM2PrivateKey));
        }

        /// <summary>
        /// 非对称加密算法SM2解密
        /// </summary>
        /// <param name="cipherData">解密字节流</param>
        /// <param name="privateKey">私钥字节流</param>
        /// <returns></returns>
        private static byte[] SM2_Decrypt(byte[] cipherData, byte[] privateKey)
        {
            var curve = GMNamedCurves.GetByName("sm2p256v1");
            var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
            var d = new BigInteger(1, privateKey);
            var privKey = new ECPrivateKeyParameters(d, domainParams);

            var engine = new SM2Engine();
            engine.Init(false, privKey);

            return engine.ProcessBlock(cipherData, 0, cipherData.Length);
        }

        #endregion

        #region 哈希算法

        /// <summary>
        /// SHA256哈希 推荐，全球主流
        /// </summary>
        public static string SHA256_Hash(string input)
        {
            using var sha256 = SHA256.Create();
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);
            byte[] hashBytes = sha256.ComputeHash(inputBytes);

            var sb = new StringBuilder();
            foreach (byte b in hashBytes) sb.Append(b.ToString("x2"));
            return sb.ToString();
        }

        /// <summary>
        /// MD5哈希 (已过时，不推荐)
        /// </summary>
        public static string MD5_Hash(string input)
        {
            using var md5 = MD5.Create();
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);
            byte[] hashBytes = md5.ComputeHash(inputBytes);

            var sb = new StringBuilder();
            foreach (byte b in hashBytes) sb.Append(b.ToString("x2"));
            return sb.ToString();
        }

        #endregion

        public void Dispose()
        {
            // 清理资源
        }
    }
}
